﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace GeneticAlgorythm.Algorythm
{
    public class TournamentSelection : Selection
    {
        private Population _population;
        private List<bool> _wasIndividualUsed;
        private List<Individual> _individuals;

        public TournamentSelection()
        {
        }

        public void addPopulation(Population population)
        {
            _population = population;
        }

        public List<Individual> select(int percentageOfRemainingIndividuals)
        {
            _individuals = new List<Individual>();

            initializeWasIndividualUsed();

            int targetPopulationSize = _population.getSize() * percentageOfRemainingIndividuals / 100;

            while (targetPopulationSize > _individuals.Count)
            {
                insertNewIndividual();
            }
            return _individuals;
        }

        private void initializeWasIndividualUsed()
        {
            _wasIndividualUsed = new List<bool>();
            for (int i = 0; i < _population.getSize(); i++)
            {
                _wasIndividualUsed.Add(false);
            }
        }

        private void insertNewIndividual()
        {
            List<Individual> individualsToFight = getThreeUnusedIndividuals();

            _individuals.Add(findBestFrom(individualsToFight));

        }

        private List<Individual> getThreeUnusedIndividuals()
        {
            List<Individual> individuals = new List<Individual>();
            for (int i = 0; i < 3; i++)
            {
                int index = RandomGenerator.getInstance().generateRandomIntegerFromZeroTo(_population.getSize());
                while (_wasIndividualUsed.ElementAt(index))
                    index = RandomGenerator.getInstance().generateRandomIntegerFromZeroTo(_population.getSize());
                individuals.Add(_population.getIndividual(index));
                _wasIndividualUsed[index] = true;
                if (allIndividualsWereUsed())
                    initializeWasIndividualUsed();
            }
            return individuals;
            
        }

        private bool allIndividualsWereUsed()
        {
            foreach(bool wasUsed in _wasIndividualUsed){
                if (wasUsed.Equals(false))
                    return false;
            }
            return true;
        }

        private Individual findBestFrom(List<Individual> individualsToFight)
        {
            int bestIndex=0;
            for (int i = 1; i < individualsToFight.Count; i++)
            {
                if (individualsToFight.ElementAt(i).getObjectiveFunctionValue() <
                    individualsToFight.ElementAt(bestIndex).getObjectiveFunctionValue())
                    bestIndex = i;
            }
            return individualsToFight.ElementAt(bestIndex);
        }
    }
}
